﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TestByWire.Tutorial.Domain;

namespace TestByWire.Tutorial
{
    [TestClass]
    public class Basics : TestTarget<SalaryManager>
    {
        [TestMethod]
        public void EverythingIsInitialized()
        {
            double newSalery = When.Calling.RaiseSalary("foo");
            Assert.AreEqual(0, newSalery);
        }

        [TestMethod]
        public void SetUpDependencyBehaviour()
        {
            Given.That<ITaxationCalulator>(x => x.CalculateTax(0)).Returns(200d);

            double salery = When.Calling.RaiseSalary("foo");

            Assert.AreEqual(200, salery);
        }

        [TestMethod]
        public void AssertOnDependencyBehaviour()
        {
            var employeeInfomation = new EmployeInformation();
            Given.That<IEmployeStorage>(x => x.RetrieveCurrentSalary("foo")).Returns(employeeInfomation);
            Given.That<ITaxationCalulator>(x => x.CalculateTax(0)).Returns(200d);

            When.Calling.RaiseSalary("foo");

            Then<IEmployeStorage>.Should(x => x.UpdateSalary(employeeInfomation, 200d));

            // Pure BDD - readss like Gherkin
            // Given that IEmployeStorage.RetrieveCurrentSalary(0) returns employeeinformation (and)
            // Given that ITaxationCalculator.CalculateTax(0) returns 200
            // When calling SaleryManager.RaiseSalary("foo")
            // Then IEmployeStorage should UpdateSalary(employeeInformation, 200)
        }

        [TestMethod]
        public void IndirectRuntimeDependenciesAreConfigured()
        {
            Given.That<ISalaryStrategy>(x => x.CalculateSalaryAdjustment(0)).Returns(300d);

            double salery = When.Calling.RaiseSalary("foo");

            Assert.AreEqual(300d, salery);
        }

        [TestMethod, ExpectedException(typeof(InvalidOperationException))]
        public void ThrowException()
        {
            Given.That<ISalaryStrategy>(x => x.CalculateSalaryAdjustment(0)).Throws(new InvalidOperationException());

            When.Calling.RaiseSalary("foo");
        }

        [TestMethod]
        public void RepeatAction()
        {
            Given.That<IEmployeStorage>(x => x.RetrieveCurrentSalary("foo")).Returns(new EmployeInformation { Salary = 20 }).Repeat(2);
            Given.That<IEmployeStorage>(x => x.RetrieveCurrentSalary("foo")).Returns(new EmployeInformation { Salary = 30 });

            double[] saleries = When.Calling.GetSaleries("foo", "foo", "foo");

            Assert.AreEqual(20, saleries[0]);
            Assert.AreEqual(20, saleries[1]);
            Assert.AreEqual(30, saleries[2]);
        }

        [TestMethod]
        public void RaiseEvent()
        {
            When<IEventRaiser>.RaiseEvent(x => x.eventToRaise += null, With<IEventRaiser>.Dependency, null);

            Then<IEmployeStorage>.Should(x => x.RetrieveCurrentSalary(null));
        }

        [TestMethod]
        public void RaiseEventWithArgument()
        {
            When<IEventRaiser>.RaiseEvent(x => x.eventToRaise += null, With<IEventRaiser>.Dependency, new StringEventArgs("bar"));

            Then<IEmployeStorage>.Should(x => x.RetrieveCurrentSalary("bar"));
        }

        [TestMethod]
        public void OnlyOneAssertionPerTest()
        {
            When.Calling.GetSaleries();

            Then<IEmployeStorage>.Fulfills(x => true);
            try
            {
                Then<IEmployeStorage>.Fulfills(x => true);
                Assert.Fail("Only one assertion per test.");
            }
            catch (InvalidOperationException)
            {
            }
        }
    }
}
